import React, { StrictMode, useState, useRef, useImperativeHandle } from 'react'

const boxStyle = {
  margin: '10px',
  border: '1px solid #ccc',
  borderRadius: '10px',
  padding: '10px',
}

export class ForwardRefTemplate extends React.Component {
  constructor(props) {
    super(props)
    this.state = { num: 11 }
    this.SonForwardEle = React.createRef()
    this.SonForwardRefClass = React.createRef()
    this.FancyInputRef = React.createRef()
    this.hocComponentRef = React.createRef()
    this.levelRef = React.createRef()
  }
  componentDidMount() {
    console.log('SonForwardEle...', this.SonForwardEle)
    console.log('SonForwardRefClass...', this.SonForwardRefClass)
    console.log('FancyInput...', this.FancyInputRef)
    console.log('hocComponentRef...', this.hocComponentRef)
    console.log('levelRef...', this.levelRef)
  }
  render() {
    return (
      <div style={boxStyle}>
        <p>ref转发各种情况</p>
        <p>这是父组件</p>
        <button
          style={{ marginBottom: '10px' }}
          onClick={() =>
            this.setState((state, props) => ({
                num: state.num + 111,
            }))
          }
        >
          {this.state.num}
        </button>
        {/* forwardRef传递ref可以不使用 */}
        {true && <SonForwardEle></SonForwardEle>}
        {/* forwardRef传递ref,获取ele */}
        {true && <SonForwardEle ref={this.SonForwardEle}></SonForwardEle>}
        {/* forwardRef传递ref,获取组件 */}
        {true && <SonForwardRefClass ref={this.SonForwardRefClass}></SonForwardRefClass>}
        {/* 通过forwardRef 将函数组件内部数据方法传递出去 */}
        {true && (
          <div>
            {/* 不要这么使用refs, 在父组件中使子组件中值, current可能为nulll, 不会随子组件更新 */}
            <p>
              使用函数组件内部值:
              {this.FancyInputRef.current && this.FancyInputRef.current.state.inputStr}
            </p>
            <button
              style={{ marginBottom: '10px' }}
              onClick={() => this.FancyInputRef.current.fun.inputChange()}
            >
              改变内部值
            </button>
            <button
              style={{ marginBottom: '10px' }}
              onClick={() => this.FancyInputRef.current.fun.focusInput()}
            >
              focus内部input
            </button>
            <FancyInput ref={this.FancyInputRef} />
          </div>
        )}
        {/* ref多级转发 */}
        {true && <SonLevel1 ref={this.levelRef}></SonLevel1>}
        {/* ref穿透转发 到高阶组件 */}
        {true && <GetHocComponent ref={this.hocComponentRef}></GetHocComponent>}
      </div>
    )
  }
}


// +++++++++++++++++++++++++++++ 总结 +++++++++++++++++++++++++++++++++
// forwardRef转发的ref可以使用
// 1.绑定在ele的ref属性上
// 2.绑定在 组件 的ref属性上
// 3.作为props传递到组件内部,穿透到高阶组件内
// 4.配合useImperativeHandle获取函数组件 内部内容

// +++++++++++++++++++++++++++++ forwardRef使用函数内部数据及方法  +++++++++++++++++++++++++++++++++
// React.forwardRef(FunComponent), 使用forwardRef处理的函数组件可以接收第二个参数为, 为组件上的ref属性
// 函数组件没有实例
// 接收的ref可以在内部使用, 在内部传递给element和Component的ref属性进行绑定
function FancyInput(props, ref) {
  const [inputStr, setInputStr] = useState('哈哈哈哈')
  const inputRef = useRef()
  // 通过useImperativeHandle指定暴露给外部值
  useImperativeHandle(ref, () => ({
    state: {
      // 暴露state
      inputStr,
    },
    setState: {
      // 暴露setState
      setInputStr,
    },
    refs: {
      // 暴露refs
      inputRef,
    },
    fun: {
      // 暴露 内部方法
      focusInput,
      inputChange,
    },
  }))
  function focusInput() {
    inputRef.current.focus()
  }
  function inputChange() {
    setInputStr((preVal) => preVal + 1)
  }
  return (
    <div style={boxStyle}>
      <p>forwardRef转发获取内部数据方法</p>
      <input ref={inputRef} type="text" value={inputStr} onChange={inputChange} />
    </div>
  )
}
FancyInput = React.forwardRef(FancyInput)

// +++++++++++++++++++++++++++++ forwardRef获取函数组件内部ele +++++++++++++++++++++++++++++++++
// 常规函数和 class 组件不接收 ref 参数，且 props 中也不存在 ref。
// Ref 转发是一个可选特性，其允许某些组件接收 ref，并将其向下传递（换句话说，“转发”它）给子组件。
// 第二个参数 ref 只在使用 React.forwardRef 定义组件时存在。
const SonForwardEle = React.forwardRef((props, ref) => {
  return (
    <div style={boxStyle}>
      <p>forwardRef转发获取内部ele</p>
      <button ref={ref} className="FancyButton">
        按钮
      </button>
    </div>
  )
})

// +++++++++++++++++++++++++++++ forwardRef获取函数组件内部组件 +++++++++++++++++++++++++++++++++
class SonClass extends React.Component {
  constructor(props) {
    super(props)
    this.state = { aaa: 123456 }
  }
  render() {
    return (
      <div style={boxStyle}>
        <p>这是class子组件</p>
      </div>
    )
  }
}

// Ref 转发不仅限于 DOM 组件，你也可以转发 refs 到 class 组件实例中。
const SonForwardRefClass = React.forwardRef((props, ref) => {
  return (
    <div style={boxStyle}>
      <p>forwardRef转发获取内部 class组件</p>
      <SonClass ref={ref}></SonClass>
    </div>
  )
})

// +++++++++++++++++++++++++++++ forwardRef多级穿透 +++++++++++++++++++++++++++++++++
function SonLevel1(props, ref) {
    return (
      <div style={boxStyle}>
        <p>这是函数一级子组件</p>
        <SonLevel2 ref={ref}></SonLevel2>
      </div>
    )
}
function SonLevel2(props, ref) {
    return (
      <div style={boxStyle}>
        <p ref={ref}>这是函数二级子组件</p>
      </div>
    )
}
SonLevel1 = React.forwardRef(SonLevel1)
SonLevel2 = React.forwardRef(SonLevel2)



// +++++++++++++++++++++++++++++ 在高阶组件中使用forwardRef +++++++++++++++++++++++++++++++++
class HocInComponent extends React.Component {
    constructor(props) {
        super(props)
        this.state = { str: '这是高阶组件啊啊' }
      }
    render() {
      const {forwardedRef, ...rest} = this.props;
      // 将自定义的 prop 属性 “forwardedRef” 定义为 ref
      return <p>这是高阶组件啊啊</p>
    }
  }

// 高阶组件
function logProps(Component) {
    class LogProps extends React.Component {
      componentDidUpdate(prevProps) {
        console.log('old props:', prevProps);
        console.log('new props:', this.props);
      }
      render() {
        const {forwardedRef, ...rest} = this.props;
  
        // 将自定义的 prop 属性 “forwardedRef” 定义为 ref
        return <Component ref={forwardedRef} {...rest} />;
      }
    }
  
    // 注意 React.forwardRef 回调的第二个参数 “ref”。
    // 我们可以将其作为常规 prop 属性传递给 LogProps，例如 “forwardedRef”
    // 然后它就可以被挂载到被 LogProps 包裹的子组件上。
    return React.forwardRef((props, ref) => {
    // forwardRef转发的ref可以作为,prop向下传递
      return <LogProps {...props} forwardedRef={ref} />;
    });
}

const GetHocComponent = logProps(HocInComponent)
